﻿///////////////////////////////////////////////////////
//
// XDesigner.ORM 
//
// autohr : yuans
//
// date: 2011-2-23
//
// email  : yyf9989@hotmail.com
// 
//
///////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ComponentModel;

namespace XDesigner.ORM.DOM
{
    [Serializable()]
    public class ORMTypeInfo : ICloneable
    {
        //private static System.Collections.Hashtable myInstances = new System.Collections.Hashtable();
        //public static ORMTypeInfo GetInstance(Type t)
        //{
        //    if (myInstances.ContainsKey(t))
        //    {
        //        return (ORMTypeInfo)myInstances[t];
        //    }
        //    else
        //    {
        //        ORMTypeInfo ot = new ORMTypeInfo();
        //        ot.Load(t);
        //        myInstances[t] = ot;
        //        return ot;
        //    }
        //}

        public ORMTypeInfo()
        {
        }

        private ORMProjectInfo _Project = null;
        /// <summary>
        /// Owner project
        /// </summary>
        [System.ComponentModel.Browsable( false )]
        [System.Xml.Serialization.XmlIgnore()]
        public ORMProjectInfo Project
        {
            get { return _Project; }
            set { _Project = value; }
        }
        
        private Type _InstanceType = null;

        [System.Xml.Serialization.XmlIgnore()]
        [System.ComponentModel.Browsable( false )]
        public Type InstanceType
        {
            get
            {
                if (_InstanceType == null 
                    && _InstanceTypeName != null 
                    && _InstanceTypeName.Trim().Length > 0  )
                {
                    foreach (System.Reflection.Assembly asm 
                        in System.AppDomain.CurrentDomain.GetAssemblies())
                    {
                        _InstanceType = asm.GetType(_InstanceTypeName, false, true);
                        if (_InstanceType != null)
                        {
                            break;
                        }
                    }
                }
                return _InstanceType; 
            }
            set
            {
                _InstanceType = value; 
            }
        }

        private string _InstanceTypeName = null;
        [System.ComponentModel.DefaultValue(null)]
        public string InstanceTypeName
        {
            get { return _InstanceTypeName; }
            set { _InstanceTypeName = value; }
        }

        public bool CheckInstanceType(object instance , bool throwException )
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            Type t = instance.GetType();
            if (t.Equals(this.InstanceType) || t.IsSubclassOf(this.InstanceType))
            {
                return true;
            }
            else
            {
                if (throwException)
                {
                    throw new ArgumentException("instance type is not " + this.InstanceType.FullName);
                }
                return false;
            }
        }

        //public object CreateInstance()
        //{
        //    if (this.InstanceType == null)
        //    {
        //        throw new InvalidOperationException("InstanceType is null");
        //    }
        //    else
        //    {
        //        return System.Activator.CreateInstance(this.InstanceType);
        //    }
        //}
         

        public bool IsImplementInterface(Type type )
        {
            if (this.InstanceType != null)
            {
                foreach (Type it in this.InstanceType.GetInterfaces())
                {
                    if (it.Equals( type )
                        || it.IsSubclassOf( type ))
                    {
                        return true;
                    }

                }
            }
            return false;
        }

        [System.ComponentModel.Browsable( false )]
        public string XmlTypeName
        {
            get
            {
                if (Attribute.GetCustomAttribute(
                    this.InstanceType, 
                    typeof(System.Xml.Serialization.XmlIgnoreAttribute),
                    true) != null)
                {
                    return null;
                }
                System.Xml.Serialization.XmlTypeAttribute xt = 
                    (System.Xml.Serialization.XmlTypeAttribute)Attribute.GetCustomAttribute(
                    this.InstanceType,
                    typeof(System.Xml.Serialization.XmlTypeAttribute),
                    true);
                if (xt == null || Util.HasContent( xt.TypeName ) == false )
                {
                    string name = this.InstanceType.Name;
                    int index = name.LastIndexOf("+");
                    if (index > 0)
                    {
                        return name.Substring(index + 1);
                    }
                    else
                    {
                        return name;
                    }
                }
                else
                {
                    return xt.TypeName;
                }
            }
        }

        private string _TableName = null;
        [System.ComponentModel.DefaultValue(null)]
        public string TableName
        {
            get
            { 
                return _TableName; 
            }
            set
            {
                _TableName = value; 
            }
        }

        private ORMBooleanValue _LogReference = ORMBooleanValue.Default;
        /// <summary>
        /// Log reference
        /// </summary>
        [System.ComponentModel.DefaultValue( ORMBooleanValue.Default )]
        public ORMBooleanValue LogReference
        {
            get { return _LogReference; }
            set { _LogReference = value; }
        }

        private bool _Readonly = false;
        [System.ComponentModel.DefaultValue( false )]
        public bool Readonly
        {
            get { return _Readonly; }
            set { _Readonly = value; }
        }

        private bool _Enabled = true;

        [System.ComponentModel.DefaultValue( true )]
        public bool Enabled
        {
            get { return _Enabled; }
            set { _Enabled = value; }
        }

        private ORMBooleanValue _Buffered = ORMBooleanValue.Default;
        /// <summary>
        /// Instance of this type buffered.
        /// </summary>
        [System.ComponentModel.DefaultValue(ORMBooleanValue.Default)]
        public ORMBooleanValue Buffered
        {
            get
            {
                return _Buffered; 
            }
            set
            {
                _Buffered = value; 
            }
        }

        [System.ComponentModel.Browsable( false )]
        public string KeyDBFieldName
        {
            get
            {
                foreach (ORMPropertyInfo p in this.Properties)
                {
                    if (p.KeyField)
                    {
                        return p.DBFieldName;
                    }
                }
                return null;
            }
        }

        [System.ComponentModel.Browsable( false )]
        public ORMPropertyInfo KeyProperty
        {
            get
            {
                foreach (ORMPropertyInfo p in this.Properties)
                {
                    if (p.KeyField && p.Ignore == false )
                    {
                        return p;
                    }
                }
                return null;
            }
        }

        [System.ComponentModel.Browsable(false)]
        public string FieldNames
        {
            get
            {
                StringBuilder str = new StringBuilder();
                foreach (ORMPropertyInfo property in this.Properties)
                {
                    if (property.Ignore == false 
                        && property.DelayLoad == false 
                        && property.HasRelation == false )
                    {
                        if (str.Length > 0)
                        {
                            str.Append(" , ");
                        }
                        str.Append(property.Name);
                    }
                }
                return str.ToString();
            }
        }

        private ORMPropertyInfoList _Properties = new ORMPropertyInfoList();
        [System.Xml.Serialization.XmlArrayItem("Property" , typeof( ORMPropertyInfo ))]
        public ORMPropertyInfoList Properties
        {
            get { return _Properties; }
            set { _Properties = value; }
        }

        public int Load(Type instanceType)
        {
            if (instanceType == null)
            {
                throw new ArgumentNullException("instanceType");
            }
            _InstanceType = instanceType;
            _InstanceTypeName = instanceType.FullName;
            this.TableName = this.InstanceType.Name;

            ORMTypeAttribute ta = (ORMTypeAttribute)Attribute.GetCustomAttribute(
                this.InstanceType,
                typeof(ORMTypeAttribute), 
                true);
            if (ta != null)
            {
                if (Util.HasContent(ta.TableName))
                {
                    this.TableName = ta.TableName.Trim();
                }
                this.Buffered = ta.Buffered;
                this.LogReference = ta.LogReference;
            }

            int iCount = 0;
            foreach (PropertyInfo p in instanceType.GetProperties(
                BindingFlags.Public
                | BindingFlags.Instance))
            {
                if (Attribute.GetCustomAttribute(p, typeof(ORMIgnoreAttribute)) != null)
                {
                    // check ignore flag
                    continue;
                }
                if (p.CanRead == false || p.CanWrite == false)
                {
                    // check accessable
                    continue;
                }
                ORMPropertyInfo property = new ORMPropertyInfo();
                
                property.Name = p.Name;
                property.PropertyInfo = p;
                property.DBFieldName = p.Name;

                ORMFieldAttribute field = (ORMFieldAttribute)Attribute.GetCustomAttribute(
                    p, 
                    typeof(ORMFieldAttribute), 
                    true);
                if (field != null)
                {
                    if (Util.HasContent(field.FieldName))
                    {
                        property.DBFieldName = field.FieldName;
                    }
                    property.ExtCondition = field.ExtCondition;
                    property.DBValueType = field.DBValueType;
                    property.Format = field.Format;
                }

                if (Attribute.GetCustomAttribute(
                    p, 
                    typeof(ORMParentReferenceAttribute), 
                    true) != null)
                {
                    property.ParentReference = ORMBooleanValue.True;
                }
                if (Attribute.GetCustomAttribute(
                    p,
                    typeof(ORMKeyFieldAttribute), 
                    true) != null)
                {
                    property.KeyField = true;
                }

                if (Attribute.GetCustomAttribute(
                    p,
                    typeof(ORMDelayAttribute), 
                    true) != null)
                {
                    if( property.Overrideable == false )
                    {
                        throw new NotSupportedException(string.Format(
                            ORMStrings.PropertyMustBeVirtualOverride_Name, 
                            instanceType.FullName + "." + p.Name));
                    }
                    property.DelayLoad = true;
                }

                ORMSortAttribute sort = (ORMSortAttribute)Attribute.GetCustomAttribute(
                    p,
                    typeof(ORMSortAttribute),
                    true);
                if (sort != null)
                {
                    property.SortStyle = sort.Style;
                    property.SortPriority = sort.Priority;
                }
                ORMNewValueGenerateAttribute idg = (ORMNewValueGenerateAttribute)Attribute.GetCustomAttribute(
                    p,
                    typeof(ORMNewValueGenerateAttribute), 
                    true);
                if (idg != null)
                {
                    property.NewValueGenerate = new ORMNewValueGenerateInfo();
                    property.NewValueGenerate.Style = idg.Style;
                    property.NewValueGenerate.Format = idg.Format;
                }
                else if (property.KeyField)
                {
                    if (property.PropertyType.Equals(typeof(int))
                        || property.PropertyType.Equals(typeof(long)))
                    {
                        property.NewValueGenerate = new ORMNewValueGenerateInfo();
                        property.NewValueGenerate.Style = ORMNewValueGenerateStyle.Increase;
                    }
                }

                ORMCommandTextAttribute cmd = (ORMCommandTextAttribute)Attribute.GetCustomAttribute(
                    p, 
                    typeof(ORMCommandTextAttribute),
                    true);
                if (cmd != null)
                {
                    property.CommandText = cmd.CommandText;
                }

                ORMCopyThisReferenceAttribute copy = (ORMCopyThisReferenceAttribute)Attribute.GetCustomAttribute(
                    p,
                    typeof(ORMCopyThisReferenceAttribute),
                    true);
                if (copy != null)
                {
                    property.CopyThisReferenceTarget = copy.Target;
                }

                ORMRelationAttribute rel = (ORMRelationAttribute)Attribute.GetCustomAttribute(
                    p, 
                    typeof(ORMRelationAttribute),
                    true);
                if (rel != null)
                {
                    property.Relation = new ORMRelationInfo();
                    property.Relation.Style = rel.Style;
                    property.Relation.ChildPropertyName = rel.ChildFieldName;
                    property.Relation.ParentPropertyName = rel.ParentFieldName;
                    property.Relation.UniteDeleteForDelete = rel.UniteDeleteForDelete;
                    property.Relation.UniteDeleteForUpdate = rel.UniteDeleteForUpdate;
                    property.Relation.LogReference = rel.LogReference;
                }

                if (Attribute.GetCustomAttribute(p, typeof(ORMReadonlyAttribute), true) != null)
                {
                    property.DBReadonly = true;
                }

                ORMFieldAttribute f = (ORMFieldAttribute)Attribute.GetCustomAttribute(
                    p,
                    typeof(ORMFieldAttribute),
                    true);
                if (f != null)
                {
                    if (Util.HasContent(f.FieldName))
                    {
                        property.DBFieldName = f.FieldName;
                    }
                }

                DescriptionAttribute descript = (DescriptionAttribute)Attribute.GetCustomAttribute(
                    p,
                    typeof(DescriptionAttribute),
                    true);
                if (descript != null)
                {
                    property.Description = descript.Description;
                }
                this.Properties.Add(property);
                iCount++;
            }//foreach
            foreach (ORMPropertyInfo p in this.Properties)
            {
                if (p.KeyField && p.NewValueGenerate == null )
                {
                    p.NewValueGenerate = new ORMNewValueGenerateInfo();
                }
                if (p.Relation != null)
                {
                    // fix dbfield name of relation property
                    ORMFieldAttribute field = (ORMFieldAttribute)Attribute.GetCustomAttribute(
                        p.PropertyInfo ,
                        typeof(ORMFieldAttribute),
                        true);
                    if (field != null && Util.HasContent(field.FieldName))
                    {
                        p.DBFieldName = field.FieldName;
                    }
                    else
                    {
                        p.DBFieldName = this.KeyDBFieldName;
                    }
                }
            }
            UpdateState();
            return iCount;
        }

        internal string GetDBFieldList()
        {
            StringBuilder str = new StringBuilder();
            foreach (ORMPropertyInfo p in this.Properties)
            {
                if (p.Ignore)
                {
                    continue;
                }
                if (Util.HasContent(p.DBFieldName) == false)
                {
                    continue;
                }
                if (p.DelayLoad)
                {
                    Type pt = p.PropertyType;
                    if (pt.Equals(typeof(string))
                        || pt.Equals(typeof(byte[])))
                    {
                        continue;
                    }
                }
                if (str.Length > 0)
                {
                    str.Append(" , ");
                }
                str.Append(p.DBFieldName);
            }
            return str.ToString();
        }

        internal string GetSelectCommandText()
        {
            return "Select " + GetDBFieldList() + " From " + this.TableName;
        }
 
        /// <summary>
        /// Merge orm mapping information
        /// </summary>
        /// <param name="type">another type information</param>
        /// <returns>Wether operation successed.</returns>
        public bool Merge(ORMTypeInfo type)
        {
            if (type == null || type == this )
            {
                return false;
            }
            if (string.Compare(type.InstanceTypeName, this.InstanceTypeName, true) != 0)
            {
                return false;
            }
            if (type._Enabled != this._Enabled)
            {
                this._Enabled = type._Enabled;
            }
            if (type.Buffered == ORMBooleanValue.True)
            {
                this.Buffered = ORMBooleanValue.True;
            }
            else if (type.Buffered == ORMBooleanValue.False)
            {
                this.Buffered = ORMBooleanValue.False;
            }
            if (type.LogReference != ORMBooleanValue.Default)
            {
                this.LogReference = type.LogReference;
            }
            if (type._Readonly != this._Readonly)
            {
                this._Readonly = type._Readonly;
            }
            
            foreach (ORMPropertyInfo p1 in type.Properties)
            {
                ORMPropertyInfo p2 = this.Properties[p1.Name];
                if (p2 == null)
                {
                    p2 = p1.Clone();
                    this.Properties.Add(p2);
                }
                else
                {
                    if (p1.Relation != null)
                    {
                        if (p2.Relation == null)
                        {
                            p2.Relation = new ORMRelationInfo();
                        }
                        if (Util.HasContent(p1.Relation.ChildPropertyName))
                        {
                            p2.Relation.ChildPropertyName = p1.Relation.ChildPropertyName;
                        }
                        if (Util.HasContent(p1.Relation.RuntimeParentPropertyName))
                        {
                            p2.Relation.RuntimeParentPropertyName = p1.Relation.RuntimeParentPropertyName;
                        }
                        if (p1.Relation.UniteDeleteForDelete)
                        {
                            p2.Relation.UniteDeleteForDelete = true;
                        }
                        if (p2.Relation.UniteDeleteForUpdate)
                        {
                            p2.Relation.UniteDeleteForUpdate = true;
                        }
                        if (p1.Relation.LogReference != p2.Relation.LogReference)
                        {
                            p2.Relation.LogReference = p1.Relation.LogReference;
                        }
                        p2.Relation.Style = p1.Relation.Style;
                    }

                    if (p1.DBValueType != ORMDBValueType.Default)
                    {
                        p2.DBValueType = p1.DBValueType;
                    }
                    p2.SortStyle = p1.SortStyle;

                    if (Util.HasContent(p1.CommandText))
                    {
                        p2.CommandText = p1.CommandText;
                    }
                    if (Util.HasContent(p1.DBFieldName))
                    {
                        p2.DBFieldName = p1.DBFieldName;
                    }
                    if (p1.DelayLoad)
                    {
                        p2.DelayLoad = p1.DelayLoad;
                    }
                    if (p1.ExtCondition != ORMBooleanValue.Default)
                    {
                        p2.ExtCondition = p1.ExtCondition;
                    }

                    if (p1.ParentReference != ORMBooleanValue.Default)
                    {
                        p2.ParentReference = p1.ParentReference;
                    }

                    if (p1.NewValueGenerate != null)
                    {
                        if (p2.NewValueGenerate == null)
                        {
                            p2.NewValueGenerate = p1.NewValueGenerate.Clone();
                        }
                        else
                        {
                            p2.NewValueGenerate.Style = p1.NewValueGenerate.Style;
                            if (Util.HasContent(p1.NewValueGenerate.Format))
                            {
                                p2.NewValueGenerate.Format = p1.NewValueGenerate.Format;
                            }
                        }
                    }
                    if (p1.Ignore)
                    {
                        p2.Ignore = p1.Ignore;
                    }
                    if (p1.KeyField)
                    {
                        p2.KeyField = p1.KeyField;
                    }
                    if (Util.HasContent(p1.CopyThisReferenceTarget))
                    {
                        p2.CopyThisReferenceTarget = p1.CopyThisReferenceTarget;
                    }
                }//else
                 
            }//foreach
            UpdateState();
            return true;
        }

        internal void UpdateState()
        {
            for( int iCount = this.Properties.Count - 1 ; iCount >= 0 ; iCount -- )
            {
                ORMPropertyInfo property = this.Properties[iCount];
                property.OwnerType = this;
                property.PropertyInfo = this.InstanceType.GetProperty(
                    property.Name, 
                    BindingFlags.Public 
                    | BindingFlags.Instance 
                    | BindingFlags.IgnoreCase);
                if (property.Relation != null)
                {
                    property.Relation.OwnerProperty = property;
                }
                if (property.PropertyInfo == null)
                {
                    this.Properties.RemoveAt(iCount);
                    continue;
                }
                if (property.PropertyInfo.CanWrite == false 
                    || property.PropertyInfo.CanWrite == false)
                {
                    this.Properties.RemoveAt(iCount);
                    continue;
                }
                ParameterInfo[] ps = property.PropertyInfo.GetIndexParameters();
                if (ps != null && ps.Length > 0)
                {
                    this.Properties.RemoveAt(iCount);
                    continue;
                }

                if (property.Relation != null )
                {
                    if (Util.HasContent(property.Relation.ParentPropertyName ) == false)
                    {
                        if (Util.HasContent(property.DBFieldName))
                        {
                            property.Relation.RuntimeParentPropertyName = property.DBFieldName;
                        }
                        else
                        {
                            property.Relation.RuntimeParentPropertyName = this.KeyDBFieldName;
                        }
                    }

                    property.Relation.RuntimeStyle = property.Relation.Style;
                    if (property.Relation.Style == ORMRelationStyle.Auto)
                    {
                        Type pt = property.PropertyInfo.PropertyType;
                        if (CollectionTypeHelper.IsCollectionType(pt))
                        {
                            property.Relation.RuntimeStyle = ORMRelationStyle.OneToMany;
                        }
                        else
                        {
                            if (pt.IsPrimitive)
                            {
                                property.Relation.RuntimeStyle = ORMRelationStyle.None;
                                throw new NotSupportedException( string.Format( ORMStrings.NotSupported_Name , pt.Name));
                            }
                            property.Relation.RuntimeStyle = ORMRelationStyle.OneToOne;
                        }
                    }
                }//if
            }//for
        }

        /// <summary>
        /// Clone instance
        /// </summary>
        /// <returns></returns>
        object ICloneable.Clone()
        {
            ORMTypeInfo t = new ORMTypeInfo();
            t._InstanceType = this._InstanceType;
            t._InstanceTypeName = this._InstanceTypeName;
            t._Enabled = this._Enabled;
            foreach (ORMPropertyInfo p in this._Properties)
            {
                t._Properties.Add(p.Clone());
            }
            t._TableName = this._TableName;
            t._Buffered = this._Buffered;
            t._Readonly = this._Readonly;
            t._LogReference = this._LogReference;
            return t;
        }

        /// <summary>
        /// Clone instance
        /// </summary>
        /// <returns></returns>
        public ORMTypeInfo Clone()
        {
            return (ORMTypeInfo)((ICloneable)this).Clone();
        }

        public override string ToString()
        {
            if (this.InstanceType != null)
            {
                return this.InstanceType.Name + "->" + this.TableName;
            }
            else
            {
                return this.InstanceTypeName + "->" + this.TableName;
            }
        }
         
    }

    [Serializable()]
    [System.Diagnostics.DebuggerDisplay("ORMTypeList Count={ Count }")]
    [System.Diagnostics.DebuggerTypeProxy(typeof( ORMDebugView))]
    public class ORMTypeInfoList : System.Collections.CollectionBase
    {
        public ORMTypeInfo this[int index]
        {
            get
            {
                return (ORMTypeInfo)this.List[index];
            }
        }

        public ORMTypeInfo this[Type t]
        {
            get
            {
                if (t == null)
                {
                    return null;
                }
                foreach (ORMTypeInfo item in this)
                {
                    if (item.InstanceType.Equals(t) || t.IsSubclassOf(item.InstanceType))
                    {
                        return item;
                    }
                }
                return null;
            }
        }

        public ORMTypeInfo this[string name]
        {
            get
            {
                foreach (ORMTypeInfo item in this)
                {
                    if (string.Compare(item.InstanceTypeName, name, true) == 0)
                    {
                        return item;
                    }
                }
                return null;
            }
        }

        public bool Contains(ORMTypeInfo item)
        {
            return this.List.Contains(item);
        }

        public bool Contains(Type type)
        {
            return this[type] != null;
        }

        public int Add(ORMTypeInfo type)
        {
            return this.List.Add(type);
        }

        public void Remove(ORMTypeInfo item)
        {
            this.List.Remove(item);
        }

    }
    
}
